package org.javahispano.javacup.tacticas_aceptadas.lechal.com.lamboratory.javacup;

import java.util.LinkedList;
import java.util.List;
import java.util.Random;

import org.javahispano.javacup.modelo.Comando;
import org.javahispano.javacup.modelo.ComandoGolpearBalon;
import org.javahispano.javacup.modelo.ComandoIrA;
import org.javahispano.javacup.modelo.Constantes;
import org.javahispano.javacup.modelo.JugadorDetalle;
import org.javahispano.javacup.modelo.Posicion;
import org.javahispano.javacup.modelo.SituacionPartido;
import org.javahispano.javacup.modelo.Tactica;
import org.javahispano.javacup.modelo.TacticaDetalle;

public class Lechal implements Tactica {

	protected int iteracionesSaque = 0;
	protected Random r = new Random();

	protected static final double RADIO_PORTERO = Constantes.LARGO_ARCO/2d;
	protected static final double RADIO_DEFENSA = 5;
	protected static final double LEJOS = 2.5*Constantes.DISTANCIA_PENAL;
	protected static final double ANGULO = 50d*2d*Math.PI/360d;
	protected static final double ANGULO_MAX = Math.PI/2d+ANGULO;
	protected static final double ANGULO_MIN = Math.PI/2d-ANGULO;

	protected static final int LIMITE_ITERACIONES = 40;
	protected static final int PORCENTAJE_TIRO_LEJANO = 50;
	protected static final int PORCENTAJE_PASE_DELANTEROS = 10;

	protected Posicion balonAnterior = null;
	protected double alturaAnterior = 0;

	protected static TacticaDetalle detalle = new LechalDetalle();
	
	protected Posicion[] alineacion = LechalAlineaciones.delantero;

	List<int[]> jugadasEnsayadas = new LinkedList<int[]>();
	
	public Lechal()
	{
		int[] jugada1 = {8, 580, 600, (int)(-2.0d*Constantes.ANCHO_AREA_CHICA), (int)(Constantes.LARGO_CAMPO_JUEGO/2.0d-5)};
		int[] jugada2 = {8, 600, 660, (int)(2.0d*Constantes.ANCHO_AREA_CHICA), (int)(Constantes.LARGO_CAMPO_JUEGO/2.0d-5)};
		jugadasEnsayadas.add(jugada1);
		jugadasEnsayadas.add(jugada2);
	}

	@Override
	public List<Comando> ejecutar(SituacionPartido sp) {
		
		// Compruebo si es hora de cambiar la tactica
		if(sp.iteracion()==1200)
		{
			if(sp.golesPropios()<sp.golesRival() || sp.golesPropios()==0)
			{
				alineacion = LechalAlineaciones.normal;
			}
		}
		
		List<Comando> com = new LinkedList<Comando>();

		Posicion balonSiguiente = sp.balon();
		if(balonAnterior!=null && !sp.saco() && !sp.sacaRival())
		{
			balonSiguiente = sp.balon().moverAngulo(balonAnterior.angulo(sp.balon()), 1*balonAnterior.distancia(sp.balon()));
		}
		// Portero
		double anguloPortero = Constantes.centroArcoInf.angulo(balonSiguiente);
		Posicion pPortero = Constantes.centroArcoInf.moverAngulo(anguloPortero, RADIO_PORTERO);
		if(pPortero.getY()>Constantes.centroArcoInf.getY()+Constantes.DISTANCIA_CONTROL_BALON_PORTERO)
			pPortero = pPortero.setPosicion(pPortero.getX(), Constantes.centroArcoInf.getY()+Constantes.DISTANCIA_CONTROL_BALON_PORTERO);
		com.add(new ComandoIrA(0, pPortero));
		// Jugadores
		for(int i=1; i<11; i++)
		{
			Posicion p = alineacion[i];
			if(1<=i && i<=7)
			{
				int rival = p.indiceMasCercano(sp.rivales());
				p = p.moverAngulo(p.angulo(sp.rivales()[rival]), RADIO_DEFENSA);
			}
			else if(i==8)
			{
				p = p.moverAngulo(p.angulo(sp.balon()), RADIO_DEFENSA/2);
			}
			else
			{
				p = p.moverAngulo(p.angulo(sp.balon()), RADIO_DEFENSA);
			}
			p = p.setDentroCampoJuego();
			com.add(new ComandoIrA(i, p));
		}

		for(int[] jugada : jugadasEnsayadas)
		{
			// jugada = [jugador, inicio, fin, x, y]
			if(jugada[1]<=sp.iteracion() && sp.iteracion()<=jugada[2])
			{
				com.add(new ComandoIrA(jugada[0], new Posicion(jugada[3], jugada[4])));
			}
		}
		
		boolean esperar = false;
		if(!sp.saco())
		{
			iteracionesSaque = 0;
		}
		else
		{
			iteracionesSaque++;
			if(iteracionesSaque<Constantes.ITERACIONES_SAQUE/2)
				esperar = true;
		}
		
		if(sp.puedenRematar().length>0) // Puedo golpear
		{
			if(sp.puedenRematarRival().length>0) // El rival puede golpear
			{
				// Pases / tiros
				for(int iRemate : sp.puedenRematar())
				{
					if(!esperar)
						com.add(golpearBalon(sp, iRemate));
				}
			}
			else
			{
				// Si solo yo puedo golpear, que lo haga solo el mejor jugador
				int iMejor = sp.puedenRematar()[0];
				JugadorDetalle jMejor = sp.detalleMisJugadores()[iMejor];
				for(int iRemate : sp.puedenRematar())
				{
					JugadorDetalle jRemate = sp.detalleMisJugadores()[iRemate];
					if((jRemate.getRemate()>jMejor.getRemate())
							|| (jRemate.getRemate()==jMejor.getRemate() && jRemate.getPresicion()>jMejor.getPresicion())
					)
					{
						iMejor = iRemate;
						jMejor = sp.detalleMisJugadores()[iMejor];
					}
				}
				if(!esperar)
					com.add(golpearBalon(sp, iMejor));
			}
		}
		/*else if(sp.puedenRematarRival().length>0)
		{
			// TODO: Defender
			int i = sp.balon().indiceMasCercano(sp.misJugadores());
			com.add(new ComandoIrA(i, sp.balon()));
		}*/
		else if(balonAnterior!=null)
		{
			boolean llega = false;
			for(int iteracion = 1; !llega && iteracion<LIMITE_ITERACIONES; iteracion++)
			{
				PosicionYAltura pBalonIteracion = simulaBalon(balonAnterior, alturaAnterior, sp, iteracion);
				for(int iJugador=0; iJugador<11; iJugador++)
				{
					// Llega
					JugadorDetalle jJugador = sp.detalleMisJugadores()[iJugador];
					double controlH = Constantes.DISTANCIA_CONTROL_BALON;
					double controlV = Constantes.ALTURA_CONTROL_BALON;
					if(jJugador.esPortero())
					{
						controlH = Constantes.DISTANCIA_CONTROL_BALON_PORTERO;
						controlV = Constantes.ALTO_ARCO;
					}
					if((sp.misJugadores()[iJugador].distancia(pBalonIteracion.posicion)-controlH)/iteracion<=Constantes.getVelocidad(jJugador.getVelocidad()))
					{
						if(pBalonIteracion.altura<=controlV)
						{
							llega = true;
							com.add(new ComandoIrA(iJugador, pBalonIteracion.posicion));
						}
					}
				}
			}
		}

		// Guarda el estado
		balonAnterior = sp.balon();
		alturaAnterior = sp.alturaBalon();
		return com;
	}

	@Override
	public TacticaDetalle getDetalle() {
		return detalle;
	}

	@Override
	public Posicion[] getPosicionRecive(SituacionPartido sp) {
		return LechalAlineaciones.sacan;
	}

	@Override
	public Posicion[] getPosicionSaca(SituacionPartido sp) {
		return LechalAlineaciones.saco;
	}

	protected PosicionYAltura simulaBalon(Posicion balonAnterior, double alturaAnterior, SituacionPartido sp, int iteracion)
	{
		Posicion balon = sp.balon();
		double alturaBalon = sp.alturaBalon();

		double balonDx = balon.getX()-balonAnterior.getX();
		double balonDy = balon.getY()-balonAnterior.getY();
		double balonDz = alturaBalon-alturaAnterior;

		if(alturaAnterior==0)
		{
			balonDx = balonDx * Constantes.FACTOR_DISMINUCION_VEL_BALON_SUELO;
			balonDy = balonDy * Constantes.FACTOR_DISMINUCION_VEL_BALON_SUELO;
		}
		else
		{
			balonDx = balonDx * Constantes.FACTOR_DISMINUCION_VEL_BALON_AIRE;
			balonDy = balonDy * Constantes.FACTOR_DISMINUCION_VEL_BALON_AIRE;
			balonDz = redondeaMultiplo(balonDz - Constantes.G, Constantes.G);
		}

		for(int i=0; i<iteracion; i++)
		{
			boolean suelo = false;
			if (!suelo && alturaBalon == 0 && balonDz >= 0 && balonDz < Constantes.G * 3) //condicion para que se arrastre
			{
				suelo = true;
			}
			if (suelo)
			{
				balon = balon.moverPosicion(balonDx, balonDy);
				balonDx = balonDx * Constantes.FACTOR_DISMINUCION_VEL_BALON_SUELO;
				balonDy = balonDy * Constantes.FACTOR_DISMINUCION_VEL_BALON_SUELO;
			}
			else
			{
				alturaBalon = redondeaMultiplo(alturaBalon + balonDz, Constantes.G);
				balon = balon.moverPosicion(balonDx, balonDy);
				balonDz = redondeaMultiplo(balonDz - Constantes.G, Constantes.G);
				balonDx = balonDx * Constantes.FACTOR_DISMINUCION_VEL_BALON_AIRE;
				balonDy = balonDy * Constantes.FACTOR_DISMINUCION_VEL_BALON_AIRE;
				if (alturaBalon == 0)
				{
					balonDz = (-balonDz - Constantes.G) * Constantes.FACTOR_DISMINUCION_ALTURA_BALON_REBOTE;
					balonDz = redondeaMultiplo(balonDz, Constantes.G);
				}
			}
		}
		balon = balon.setDentroCampoJuego();
		return new PosicionYAltura(balon, alturaBalon);
	}

	protected double redondeaMultiplo(double valor, double divisor) {
		return Math.round(valor / divisor) * divisor;
	}

	protected class PosicionYAltura
	{
		Posicion posicion = null;
		double altura = 0;
		public PosicionYAltura(Posicion posicion, double altura) {
			super();
			this.posicion = posicion;
			this.altura = altura;
		}
	}

	protected Comando golpearBalon(SituacionPartido sp, int iJugador)
	{
		Posicion p = Constantes.centroArcoSup;
		int anguloZ = 15;
		double fuerza = 1d;
		switch(iJugador+1)
		{
		case 1:
			p = sp.misJugadores()[7-1];
		case 2:
		case 3:
		case 4:
		case 5:
			// Despeje
			p = sp.misJugadores()[7-1];
			anguloZ = 32;
			break;
		case 7:
		case 9:
			Posicion pPorteroRival = null;
			for(int i=0; i<11; i++)
			{
				JugadorDetalle jDetalle = sp.detalleJugadoresRivales()[i];
				if(jDetalle.esPortero())
					pPorteroRival = sp.rivales()[i];
			}
			if(sp.misJugadores()[iJugador].getY()>Constantes.LARGO_CAMPO_JUEGO/2.0d-Constantes.DISTANCIA_PENAL)
			{
				if(Math.abs(sp.misJugadores()[iJugador].getX())<Constantes.LARGO_ARCO)
				{
					// Dentro del area -> tira
					p = Constantes.centroArcoSup.moverPosicion(0, Constantes.DISTANCIA_PENAL);
					break;
				}
			}
			else if(sp.misJugadores()[iJugador].getY()>20 && r.nextInt(100)<PORCENTAJE_TIRO_LEJANO && (pPorteroRival.getY()<=Constantes.LARGO_CAMPO_JUEGO-Constantes.DISTANCIA_CONTROL_BALON_PORTERO || r.nextInt(100)<25))
			{
				// Disparo desde lejos
				double distancia = sp.misJugadores()[iJugador].distancia(Constantes.centroArcoSup);
				if(distancia>30.0d)
					anguloZ = 29;
				else if(distancia>25.0d)
					anguloZ = 26;
				else if(distancia>20.0d)
					anguloZ = 23;
				else if(distancia>16.0d)
					anguloZ = 20;
				else
					anguloZ = 18;
				p = Constantes.centroArcoSup;
				//System.out.println("Tiro con angulo "+anguloZ+"! "+sp.iteracion()+" "+sp.misJugadores()[iJugador].getY()+" "+sp.misJugadores()[iJugador].getX());
				
				if(iJugador+1==7)
				{
					int[] jugada = {8, sp.iteracion(), sp.iteracion()+30, (int)(Constantes.ANCHO_AREA_CHICA), (int)sp.misJugadores()[8].getY()};
					jugadasEnsayadas.add(jugada);
				}
				break;
			}
		case 6:
		case 8:
			// Pase
			p = sp.misJugadores()[11-r.nextInt(2)-1];
			p = p.moverAngulo(p.angulo(Constantes.centroArcoSup.moverPosicion(0, Constantes.DISTANCIA_PENAL)), 5);
			if(sp.misJugadores()[iJugador].distancia(p)<10)
				fuerza = 0.65d;
			else if(sp.misJugadores()[iJugador].distancia(p)<20)
				fuerza = 0.80d;
			else if(sp.misJugadores()[iJugador].distancia(p)<25)
				fuerza = 0.90d;
			break;
		case 10:
		case 11:
			// Apunta al palo corto
			if(sp.misJugadores()[iJugador].getX()>0)
				p = p.moverPosicion(Constantes.LARGO_ARCO/4, 0);
			else
				p = p.moverPosicion(-Constantes.LARGO_ARCO/4, 0);
			
			double angulo = sp.misJugadores()[iJugador].angulo(p);
			if(angulo>ANGULO_MAX || angulo<ANGULO_MIN || Constantes.centroArcoSup.distancia(sp.misJugadores()[iJugador])>LEJOS || r.nextInt(100)<PORCENTAJE_PASE_DELANTEROS)
			{
				// Pase adelantado
				p = sp.misJugadores()[(iJugador+1==10 ? 11-1 : 10-1)];
				//p = p.setPosicion(p.getX(), (p.getY()*0.8d+Constantes.penalSup.getY()*0.2d));
				p = p.moverAngulo(p.angulo(Constantes.centroArcoSup.moverPosicion(0, Constantes.DISTANCIA_PENAL)), 4);
				if(sp.misJugadores()[iJugador].distancia(p)<10)
					fuerza = 0.65d;
				else if(sp.misJugadores()[iJugador].distancia(p)<20)
					fuerza = 0.80d;
				else if(sp.misJugadores()[iJugador].distancia(p)<25)
					fuerza = 0.90d;
			}
			else if(sp.misJugadores()[iJugador].getY()<Constantes.penalSup.getY())
			{
				anguloZ = 16;
			}
			break;
		}
		return new ComandoGolpearBalon(iJugador, p, fuerza, anguloZ);
	}
}
